/*
 * Copyright (C) 2010 Enrique Lara (enrique.posix2.lara@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package app.tellwmata.model;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;

import android.widget.Toast;
import static app.tellwmata.Constants.FormFields.AM_PM;
import static app.tellwmata.Constants.FormFields.COMMENTS;
import static app.tellwmata.Constants.FormFields.FORM;
import static app.tellwmata.Constants.FormFields.FORM_NAME;
import static app.tellwmata.Constants.FormFields.FORM_TYPE;
import static app.tellwmata.Constants.FormFields.INCIDENT_DATE;
import static app.tellwmata.Constants.FormFields.INCIDENT_TIME;
import static app.tellwmata.Constants.FormFields.ROUTE_TYPE;

import app.tellwmata.R;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;

public final class Agency {
    private static final int ROUTE_TYPE_TRAIN = 1;
    private static final int ROUTE_TYPE_BUS = 3;
    private static int ID = -1;
    private static String NAME = "METRO";
    private static String URL = "http://wwww.wmata.com";
    private static String TIME_ZONE = "America/New_York";
    private Context context;
    private static final String RIDER_FORM_URL = "http://wmata.com/about_metro/contact_us/process_ridercomment.cfm";
    private static final HashMap<String, HashMap<String, String>> forms = new HashMap<String, HashMap<String, String>>();

    public static Agency getInstance(final Context context) {
        return new Agency(context);
    }

    private Agency(Context context) {
        this.context = context;

        if (ID == -1) {
            initAgency(context);
        }
    }

    public HashMap<String, String> getFormCfg(String name, int routeType) {
        if (forms.isEmpty()) {
            initForms(context);
        }

        String formId = name + "-" + routeType;
        HashMap<String, String> rval;

        if (forms.containsKey(formId)) {
            rval = forms.get(formId);
        } else {
            rval = new HashMap<String, String>();
        }

        return rval;
    }

    public ArrayList<HashMap<String, String>> getForms(int routeType) {
        if (forms.isEmpty()) {
            initForms(context);
        }

        ArrayList<HashMap<String, String>> rval = new ArrayList<HashMap<String, String>>(30);

        final String routeStr = Integer.toString(routeType);

        for (HashMap<String, String> form : forms.values()) {
            if (routeStr.equals(form.get(ROUTE_TYPE))) {
                rval.add(form);
            }
        }

        //XXX Maps make for very ugly sorting, consider Form Object.
        Collections.sort(rval, new Comparator<HashMap<String, String>>() {
                @Override
                public int compare(HashMap<String, String> o1, HashMap<String, String> o2) {
                    return o1.get("title").compareTo(o2.get("title"));
                }
            });

        return rval;
    }

    // train route names
    public String[] getTrainLines() {
        //        return map.keySet().toArray(new String[] {  });
        if (TRAIN_ROUTE_NAMES == null) {
            initRoutes(context);
        }

        return TRAIN_ROUTE_NAMES;
    }

    public String[] getBusRoutes() {
        if (BUS_ROUTE_NAMES == null) {
            initRoutes(context);
        }

        return BUS_ROUTE_NAMES;
    }

    private static String[] TRAIN_ROUTE_NAMES = null;
    private static String[] BUS_ROUTE_NAMES = null;

    private static synchronized void initRoutes(Context context) {
        InputStream is = null;
        BufferedReader reader = null;

        try {
            is = context.getResources().openRawResource(R.raw.routes);
            reader = new BufferedReader(new InputStreamReader(is));

            final ArrayList<Route> trainRoutes = new ArrayList<Route>(8);
            final ArrayList<Route> busRoutes = new ArrayList<Route>(310);

            String str = null;

            for (int i = 0; (str = reader.readLine()) != null; i++) {
                if (i == 0) {
                    continue;
                }

                final String[] toks = str.split(",");
                final int routeId = Integer.parseInt(toks[0]);
                final int agencyId = Integer.parseInt(toks[1]);
                final String routeShortName = toks[2].replaceAll("\"", "");
                final String routeLongName = toks[3].replaceAll("\"", "");
                final int routeType = Integer.parseInt(toks[4]);

                if (routeShortName.length() == 0) {
                    continue;
                }

                Route r = new Route(routeId, agencyId, routeShortName, routeLongName, routeType);

                switch (routeType) {
                    case ROUTE_TYPE_TRAIN:
                        trainRoutes.add(r);

                        break;

                    case ROUTE_TYPE_BUS:
                        busRoutes.add(r);

                        break;
                }
            }

            TRAIN_ROUTE_NAMES = new String[trainRoutes.size()];

            for (int i = 0; i < trainRoutes.size(); i++) {
                TRAIN_ROUTE_NAMES[i] = trainRoutes.get(i).getShortName();
            }

            BUS_ROUTE_NAMES = new String[busRoutes.size()];

            for (int i = 0; i < busRoutes.size(); i++) {
                BUS_ROUTE_NAMES[i] = busRoutes.get(i).getShortName();
            }
        } catch (IOException e) {
            App.showError(context, "Problems parsing route data", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    //Ignore Exception
                }
            }
        }
    }

    private static synchronized void initAgency(Context context) {
        InputStream is = null;
        BufferedReader reader = null;

        try {
            is = context.getResources().openRawResource(R.raw.agency);
            reader = new BufferedReader(new InputStreamReader(is));

            String str = null;

            for (int i = 0; (str = reader.readLine()) != null; i++) {
                if (i == 0) {
                    continue;
                }

                final String[] toks = str.split(",");
                ID = Integer.parseInt(toks[0]);
                NAME = toks[1].replaceAll("\"", "");
                URL = toks[2].replaceAll("\"", "");
                TIME_ZONE = toks[3].replaceAll("\"", "");

                break;
            }
        } catch (IOException e) {
            App.showError(context, "Problems parsing agency data", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    //Ignore Exception
                }
            }
        }
    }

    private static synchronized void initForms(Context context) {
        InputStream is = null;
        BufferedReader reader = null;

        try {
            final MessageMaker messageMaker = new MessageMaker(context);

            is = context.getResources().openRawResource(R.raw.forms);
            reader = new BufferedReader(new InputStreamReader(is));

            String[] columns = new String[42];

            for (int i = 0; i < columns.length; i++) {
                columns[i] = "";
            }

            String str = null;

            for (int i = 0; (str = reader.readLine()) != null; i++) {
                if (i == 0) {
                    columns = str.split(",");

                    continue;
                }

                //XXX parse form 
                final String[] toks = str.split(",");
                final HashMap<String, String> form = new HashMap<String, String>();
                form.put(FORM_NAME, "UNKNOWN");
                form.put(ROUTE_TYPE, "-1");
                form.put(FORM_TYPE, "UNKNOWN");

                //XXX
                for (int j = 0; (j < toks.length) && (j < columns.length);
                         j++) {
                    form.put(columns[j], toks[j].trim());
                }

                final String formTypeStr = form.get(ROUTE_TYPE);
                int routeType;

                try {
                    routeType = Integer.parseInt(formTypeStr);
                } catch (NumberFormatException e) {
                    routeType = -1;
                }

                form.put("title", messageMaker.getTitle(form.get(FORM_TYPE), routeType));

                final String formId = form.get(FORM_NAME) + "-" + formTypeStr;
                forms.put(formId, form);
            }
        } catch (IOException e) {
            App.showError(context, "Problems parsing form data", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    //Ignore Exception
                }
            }
        }
    }

    public String getName() {
        return NAME;
    }

    public String getUrl() {
        return URL;
    }

    public String getTimeZone() {
        return TIME_ZONE;
    }

    public boolean submitRiderForm(final String formType, int routeType, Calendar incidentDateTime, HashMap<String, String> form) {
        boolean rval = false;

        try {
            HttpURLConnection urlConnection = (HttpURLConnection) new URL(RIDER_FORM_URL).openConnection();

            // Set properties of the connection
            urlConnection.setRequestMethod("POST");
            urlConnection.setDoInput(true);
            urlConnection.setDoOutput(true);
            urlConnection.setUseCaches(rval);
            urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

            // Form the POST parameters
            final StringBuilder content = new StringBuilder();

            final String dateStr = android.text.format.DateFormat.format("(MM/dd/yyyy)", incidentDateTime).toString();
            final String timeStr = android.text.format.DateFormat.format("h:mm", incidentDateTime).toString();
            content.append(INCIDENT_DATE).append("=").append(URLEncoder.encode(dateStr, "UTF-8"));
            content.append("&").append(INCIDENT_TIME).append("=").append(URLEncoder.encode(timeStr, "UTF-8"));

            final int hourOfDay = incidentDateTime.get(Calendar.HOUR_OF_DAY);
            final String amPmStr = (hourOfDay > 12) ? "P.M." : "A.M.";
            content.append("&").append(AM_PM).append("=").append(URLEncoder.encode(amPmStr, "UTF-8"));

            final String msg = new MessageMaker(context).getRiderFormMessage(formType, routeType, form);
            content.append("&").append(COMMENTS).append("=").append(URLEncoder.encode(msg, "UTF-8"));

            //XXX clone.
            for (int i = 0; i < FORM.length; i++) {
                //XXX
                final String fieldName = FORM[i];

                if (INCIDENT_DATE.equals(fieldName) || INCIDENT_TIME.equals(fieldName) || AM_PM.equals(fieldName) || COMMENTS.equals(fieldName)) {
                    continue;
                }

                content.append("&");
                content.append(fieldName).append("=");

                final String value = form.get(fieldName);

                if (value != null) {
                    content.append(URLEncoder.encode(value, "UTF-8"));
                }
            }

            OutputStream outputStream = urlConnection.getOutputStream();
            outputStream.write(content.toString().getBytes("UTF-8"));
            outputStream.close();

            // Retrieve the output
            int responseCode = urlConnection.getResponseCode();

            if (responseCode == HttpURLConnection.HTTP_OK) {
                rval = true;

                Toast.makeText(context, R.string.feedback_submitted, Toast.LENGTH_SHORT).show();

                //XXX check response content
                //                InputStream inputStream = urlConnection.getInputStream();
                //                Toast.makeText(context, toString(inputStream), Toast.LENGTH_LONG).show();
            } else {
                InputStream inputStream = urlConnection.getErrorStream();
                Toast.makeText(context, toString(inputStream), Toast.LENGTH_LONG).show();
            }
        } catch (MalformedURLException e) {
            App.showError(context, "Problems submitting rider form", e);
            throw new RuntimeException(e);
        } catch (IOException e) {
            App.showError(context, "Problems submitting rider form", e);
        }

        return rval;
    }

    private String toString(InputStream inputStream) throws IOException {
        String string;
        StringBuilder outputBuilder = new StringBuilder();

        if (inputStream != null) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

            while (null != (string = reader.readLine())) {
                outputBuilder.append(string).append('\n');
            }
        }

        return outputBuilder.toString();
    }

    public boolean sendTwitter(final String formType, int routeType, HashMap<String, String> form) {
        final String msg = new MessageMaker(context).getTwitterMessage(formType, routeType, form);

        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.putExtra(Intent.EXTRA_TEXT, msg);
        intent.setType("application/twitter");

        try {
            context.getText(R.string.app_name);
            context.startActivity(intent);

            return true;
        } catch (ActivityNotFoundException e) {
            Toast.makeText(context, "No suitable apps found.", Toast.LENGTH_SHORT).show();
        }

        return false;
    }
}
